import math
from recordclass import recordclass

from coin.exchange.base.order_gateway import OrderSide

FillStats = recordclass('FillStats',
                        [
                            'buy_fill_qty',
                            'sell_fill_qty',
                            'buy_fill_cnt',
                            'sell_fill_cnt',
                            'consecutive_buy_fill_qty',
                            'consecutive_sell_fill_qty',
                            'consecutive_buy_fill_cnt',
                            'consecutive_sell_fill_cnt',
                            'last_fill_side',
                        ])


class FillStatsCalculator(FillStats):
  def __new__(cls, buy_order_side, sell_order_side):
    return super().__new__(cls,
                           buy_fill_qty=0,
                           sell_fill_qty=0,
                           buy_fill_cnt=0,
                           sell_fill_cnt=0,
                           consecutive_buy_fill_qty=0,
                           consecutive_sell_fill_qty=0,
                           consecutive_buy_fill_cnt=0,
                           consecutive_sell_fill_cnt=0,
                           last_fill_side=OrderSide.UNKNOWN)

  def __init__(self, buy_order_side, sell_order_side):
    super().__init__()
    assert isinstance(buy_order_side, (list, tuple))
    assert isinstance(sell_order_side, (list, tuple))
    self._BuyOrderSide = tuple(buy_order_side)
    self._SellOrderSide = tuple(sell_order_side)

  @property
  def net_fill(self):
    return self.buy_fill_qty - self.sell_fill_qty

  @property
  def consecutive_fill_qty(self):
    return max(self.consecutive_buy_fill_qty, self.consecutive_sell_fill_qty)

  def add_fill(self, product, filled_qty, order_side, order_qty):
    if self.last_fill_side != order_side:
      self.consecutive_buy_fill_qty = 0
      self.consecutive_sell_fill_qty = 0
      self.consecutive_buy_fill_cnt = 0
      self.consecutive_sell_fill_cnt = 0
    self.last_fill_side = order_side
    fully_filled = order_qty and math.isclose(filled_qty, order_qty)

    if order_side in self._BuyOrderSide:
      self.buy_fill_qty += abs(filled_qty)
      self.consecutive_buy_fill_qty += abs(filled_qty)
      if fully_filled:
        self.buy_fill_cnt += 1
        self.consecutive_buy_fill_cnt += 1
    elif order_side in self._SellOrderSide:
      self.sell_fill_qty += abs(filled_qty)
      self.consecutive_sell_fill_qty += abs(filled_qty)
      if fully_filled:
        self.sell_fill_cnt += 1
        self.consecutive_sell_fill_cnt += 1
    else:
      raise ValueError('Unknown order side: %s' % order_side)


OrderAndTrade = recordclass('OrderAndTrade', ['order', 'trade'])


class FillStatsCalculator2(object):
  ProductType = None
  BuyOrderSide = None
  SellOrderSide = None

  def __init__(self, products):
    assert all([isinstance(x, self.ProductType) for x in products]), \
      (self.ProductType, [type(x) for x in products])
    self._products = products
    self._stats = {
        product.symbol: OrderAndTrade(
            FillStatsCalculator(buy_order_side=self.BuyOrderSide,
                                sell_order_side=self.SellOrderSide),
            FillStatsCalculator(buy_order_side=self.BuyOrderSide,
                                sell_order_side=self.SellOrderSide)) for product in products
    }

  def assert_type_product(self, product):
    assert isinstance(product, self.ProductType), (product, type(product))

  def add_fill_by_order(self, product, filled_qty, order_side, order_qty=None):
    self.assert_type_product(product)
    self._stats[product.symbol].order.add_fill(product, filled_qty, order_side, order_qty)

  def add_fill_by_trade(self, product, filled_qty, order_side, order_qty=None):
    self.assert_type_product(product)
    self._stats[product.symbol].trade.add_fill(product, filled_qty, order_side, order_qty)

  def get_fill_stats(self, product):
    self.assert_type_product(product)
    return self._stats[product.symbol]
